package core.util;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import net.sf.json.JSONObject;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;

public class StringUtil {

	private static final String SPLIT_DELIMITER = ",|;|\\|";
	
	private StringUtil() {
	}

	
	
	public static boolean isSame(String value1, String value2) {
		if ( value1 == null && value2 == null ) {
			return true;
		}
		
		if ( value1 == null && value2 != null ) {
			return false;
		}
		
		if ( value1 != null && value2 == null ) {
			return false;
		}
		
		return value1.equals(value2);
	}
	
	public static String emptyAsNull(String value) {
		if ( value == null ) {
			return value;
		}
		
		if (value.trim().equals("")) {
			return null;
		} else {
			return value;
		}
	}
	
	public static String nullAsEmpty(Object o){
		if(o==null){
			return "";			
		}else{
			return o.toString();
		}
	}

	public static Boolean asBoolean(String value) {
		value = emptyAsNull(value);
		if (value == null) {
			return null;
		}

		return new Boolean(value);
	}
	
	public static boolean asBool(String value, boolean whenNull) {
		value = emptyAsNull(value);
		if (value == null) {
			return whenNull;
		}

		return new Boolean(value);
	}

	public static Integer asInteger(String value) {
		value = emptyAsNull(value);
		if (value == null) {
			return null;
		}

		return Integer.valueOf(value, 10);
	}
	
	public static int asInt(String value, int whenNull) {
		value = emptyAsNull(value);
		if (value == null) {
			return whenNull;
		}

		return Integer.valueOf(value, 10);
	}

	public static Object asReflect(String value) {
		try {
			int idx = value.lastIndexOf(".");
			String className = value.substring(0, idx);
			String fieldName = value.substring(idx + 1);
			Class<?> c = Class.forName(className);
			Field f = c.getField(fieldName);
			return f.get(null);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}

	}

	public static String datePattern(String dateSep) {
		return "yyyy" + dateSep + "MM" + dateSep + "dd";
	}

	public static String timePattern(String timeSep) {
		return "HH" + timeSep + "mm" + timeSep + "ss";
	}

	public static String dateTimePattern(String dateSep, String dateTimeSep, String timeSep) {
		return datePattern(dateSep) + dateTimeSep + timePattern(timeSep);
	}

	public static String format(String pattern, Object obj) {
		if (obj == null)
			return "";

		if (obj instanceof String) {
			return (String) obj;
		}

		if (pattern == null) {
			return obj.toString();
		}

		if (obj instanceof java.util.Date) {
			return format(pattern, (java.util.Date) obj);
		}

		if (obj instanceof java.math.BigDecimal) {
			return format(pattern, (java.math.BigDecimal) obj);
		}

		if (obj instanceof Double) {
			return format(pattern, (Double) obj);
		}

		if (obj instanceof Float) {
			return format(pattern, (Float) obj);
		}

		if (obj instanceof Long) {
			return format(pattern, (Long) obj);
		}

		if (obj instanceof Integer) {
			return format(pattern, (Integer) obj);
		}

		return obj.toString();
	}

	public static String format(String pattern, Locale locale, java.util.Date d) {
		if (d == null)
			return "";
		SimpleDateFormat sdf = new SimpleDateFormat(pattern, locale);
		return sdf.format(d);
	}

	public static String format(String pattern, java.util.Date d) {
		if (d == null)
			return "";
		SimpleDateFormat sdf = new SimpleDateFormat(pattern);
		return sdf.format(d);
	}

	public static String format(String pattern, java.math.BigDecimal n) {
		if (n == null)
			return "";
		DecimalFormat df = new DecimalFormat(pattern);
		return df.format(n);
	}

	public static String format(String pattern, Double n) {
		if (n == null) {
			return "";
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return df.format(n);
	}

	public static String format(String pattern, Float n) {
		if (n == null) {
			return "";
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return df.format(n);
	}

	public static String format(String pattern, Integer n) {
		if (n == null) {
			return "";
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return df.format(n);
	}

	public static String format(String pattern, Long n) {
		if (n == null) {
			return "";
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return df.format(n);
	}

	public static Date parseToDate(String pattern, Locale locale, String str) throws ParseException {
		if (str == null) {
			return null;
		}

		SimpleDateFormat formatter = new SimpleDateFormat(pattern, locale);
		return formatter.parse(str);
	}

	public static Date parseToDate(String pattern, String str) throws ParseException {
		if (str == null) {
			return null;
		}

		SimpleDateFormat formatter = new SimpleDateFormat(pattern);
		return formatter.parse(str);
	}

	public static Date parseToDateQuietly(String pattern, String str) {
		try {
			return parseToDate(pattern, str);
		} catch (Exception e) {
			return null;
		}
	}

	public static Date parseToDateQuietly(String pattern, Locale locale, String str) {
		try {
			return parseToDate(pattern, locale, str);
		} catch (Exception e) {
			return null;
		}
	}
	
	public static java.sql.Date parseToSqlDate(String pattern, String str) throws ParseException {
		if (str == null) {
			return null;
		}

		SimpleDateFormat formatter = new SimpleDateFormat(pattern);
		return new java.sql.Date(formatter.parse(str).getTime());
	}

	public static BigInteger parseToBigInteger(String pattern, String str) throws ParseException {
		if (str == null) {
			return null;
		}

		if (pattern == null) {
			return new BigInteger(str);
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return new BigInteger(df.parse(str).toString());
	}

	public static Byte parseToByte(String pattern, String str) throws ParseException {
		if (str == null) {
			return null;
		}

		if (pattern == null) {
			return Byte.valueOf(str, 10);
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return new Byte(df.parse(str).byteValue());
	}

	public static Integer parseToInteger(String pattern, String str) throws ParseException {
		if (str == null) {
			return null;
		}

		if (pattern == null) {
			return Integer.valueOf(str, 10);
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return new Integer(df.parse(str).intValue());
	}

	public static Long parseToLong(String pattern, String str) throws ParseException {
		if (str == null) {
			return null;
		}

		if (pattern == null) {
			return Long.valueOf(str, 10);
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return new Long(df.parse(str).longValue());
	}

	public static Double parseToDouble(String pattern, String str) throws ParseException {
		if (str == null) {
			return null;
		}

		if (pattern == null) {
			return new Double(str);
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return new Double(df.parse(str).doubleValue());
	}

	public static Float parseToFloat(String pattern, String str) throws ParseException {
		if (str == null) {
			return null;
		}

		if (pattern == null) {
			return new Float(str);
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return new Float(df.parse(str).floatValue());
	}

	public static Short parseToShort(String pattern, String str) throws ParseException {
		if (str == null) {
			return null;
		}

		if (pattern == null) {
			return Short.valueOf(str, 10);
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return new Short(df.parse(str).shortValue());
	}

	public static BigDecimal parseToBigDecimal(String pattern, String str) throws ParseException {
		if (str == null) {
			return null;
		}

		if (pattern == null) {
			return new BigDecimal(str);
		}

		DecimalFormat df = new DecimalFormat(pattern);
		return new BigDecimal(df.parse(str).doubleValue());
	}

	public static String parseToString(String str) {
		if (str == null) {
			return null;
		}

		str = str.trim();

		if (str.equals("")) {
			return null;
		}

		return str;
	}

	public static String string2Hex(String str, String charset) {
		if (str == null) {
			return null;
		}
		
		try {
			byte[] bytes;
			if (charset != null) {
				bytes = str.getBytes(charset);
				
			} else {
				bytes = str.getBytes();
			}
			
			StringBuilder hex = new StringBuilder();
			for (byte b : bytes) {
				String hs = Integer.toHexString(b & 0xFF).toUpperCase();
				if (hs.length() == 1) {
					hex.append("0");
				}
				hex.append(hs);
			}

			return hex.toString();
		} catch (Exception e1) {
			return null;
		}
	}

	public static String hex2String(String hex, String charset) {
		if (isBlank(hex) || hex.length() % 2 != 0) {
			return null;
		}

		try {
			byte[] bytes = new byte[hex.length() / 2];
			for (int i = 0; i < bytes.length; i++) {
				bytes[i] = (byte) (0xFF & Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16));
			}

			return (charset == null ? new String(bytes) : new String(bytes, charset));
		} catch (Exception e1) {
			return null;
		}
	}

	public static Object[] parseMessage(String pattern, String msg) {
		try {
			return new MessageFormat(pattern).parse(msg);
		} catch (Exception e) {
			return null;
		}
	}

	public static String formatMessage(String pattern, Object... args) {
		try {
			return MessageFormat.format(pattern, args);
		} catch (Exception ex) {
			return null;
		}
	}

	public static String formatString(String format, Object... args) {
		try {
			return String.format(format, args);
		} catch (Exception ex) {
			return null;
		}
	}

	public static boolean isEmpty(String str) {
		return (str == null || str.length() == 0);
	}

	public static boolean isNotEmpty(String str) {
		return (str != null && str.length() > 0);
	}

	public static boolean isBlank(String str) {
		return (str == null || str.trim().length() == 0);
	}

	public static boolean isNotBlank(String str) {
		return (str != null && str.trim().length() > 0);
	}

	public static String defaultString(String str) {
		return defaultString(str, StringUtils.EMPTY);
	}

	public static String defaultString(String str, String defVal) {
		return (str == null ? defVal : str);
	}

	public static String defaultStringIfEmpty(String str, String defVal) {
		return (isEmpty(str) ? defVal : str);
	}

	public static String defaultStringIfBlank(String str, String defVal) {
		return (isBlank(str) ? defVal : str);
	}
	
	public static String join(String separator, Object... objs) {
		return StringUtils.join(objs, separator);
	}

	public static String[] split(String msg) {
		return split(msg, SPLIT_DELIMITER);
	}

	public static String[] split(String msg, String delimiter) {
		if (msg == null) {
			return null;
		}
		delimiter = StringUtil.defaultString(delimiter, SPLIT_DELIMITER);
		return msg.split(delimiter);
	}

	public static <T extends Collection<String>> T split(String msg, Class<? extends T> clazz) {
		return split(msg, SPLIT_DELIMITER, clazz);
	}

	@SuppressWarnings("unchecked")
	public static <T extends Collection<String>> T split(String msg, String delimiter, Class<? extends T> clazz) {
		if (msg == null || clazz == null) {
			return null;
		}

		delimiter = StringUtil.defaultString(delimiter, SPLIT_DELIMITER);
		T collection = (T) CollectionUtil.newCollection(clazz);
		
		if (collection != null) {
			for (String part : msg.split(delimiter)) {
				collection.add(part);
			}
		}

		return collection;
	}

	public static String convertObjectToJson(Object obj) {
		if (obj == null) {
			return StringUtils.EMPTY;
		}

		return JSONObject.fromObject(obj).toString();
	}

	public static String[] subStringBetween(String str, String tag) {
		return subStringBetween(str, tag, tag);
	}

	public static String[] subStringBetween(String str, String open, String close) {
		return subStringBetween(str, open, close, false);
	}
	
	
	public static String replaceString(String str, String target, String[] replacement) {
		return replaceStringBetween(str, target, target, replacement, true);
	}
	
	public static String replaceString(String str, String open, String close, String[] replacement) {
		return replaceStringBetween(str, open, close, replacement, true);
	}
	
	public static String replaceStringBetween(String str, String target, String[] replacement) {
		return replaceStringBetween(str, target, target, replacement, false);
	}
	
	public static String replaceStringBetween(String str, String open, String close, String[] replacement) {
		return replaceStringBetween(str, open, close, replacement, false);
	}
	
	public static String[] subXML(String xml, String nodeName) {
		return subXML(xml, nodeName, true);
	}
	
	public static String[] subXMLBetween(String xml, String nodeName) {
		return subXML(xml, nodeName, false);
	}
	
	public static String replaceXML(String str, String nodeName, String[] replacement) {
		return replaceXML(str, nodeName, replacement, true);
	}
	
	public static String replaceXMLBetween(String str, String nodeName, String[] replacement) {
		return replaceXML(str, nodeName, replacement, false);
	}
	
	static String[] subXML(String xml, String nodeName, boolean isNodeIncluded) {
		if (xml == null || nodeName == null) {
            return ArrayUtils.EMPTY_STRING_ARRAY;
        }
		
		String startTag = new StringBuilder("<").append(nodeName).append(">").toString();
		String endTag = new StringBuilder("</").append(nodeName).append(">").toString();
        
		return subStringBetween(xml, startTag, endTag, isNodeIncluded);
	}
	
	static String replaceXML(String xml, String nodeName, String[] replacement, boolean isNodeIncluded) {
		if (xml == null || nodeName == null || ArrayUtil.isEmpty(replacement)) {
			return xml;
		}

		String startTag = new StringBuilder("<").append(nodeName).append(">").toString();
		String endTag = new StringBuilder("</").append(nodeName).append(">").toString();

		return replaceStringBetween(xml, startTag, endTag, replacement, isNodeIncluded);
	}
	
	private static String[] subStringBetween(String str, String open, String close, boolean isTagIncluded) {
		if (str == null || open == null || close == null) {
            return ArrayUtils.EMPTY_STRING_ARRAY;
        }
		
		List<String> results = new ArrayList<String>();
        int openStart = 0, openEnd = 0, closeStart = 0, closeEnd = 0;
        while (true) {
        	// ... ^open ... close ...
        	openStart = str.indexOf(open, closeEnd);
        	if (openStart == -1) { break; }
        	
        	// ... open^ ... close ...
        	openEnd = openStart + open.length();
        	
        	// ... open ... ^close ...
        	closeStart = str.indexOf(close, openEnd);
        	if (closeStart == -1) { break; }
        	
        	// ... open ... close^ ...
        	closeEnd = closeStart + close.length();
        	if (isTagIncluded) {
        		// ... [open ... close] ...
        		results.add(str.substring(openStart, closeEnd));
        		
        	} else {
        		// ... open[ ... ]close ...
        		results.add(str.substring(openEnd, closeStart));
        	}
        }
        
        return results.toArray(new String[results.size()]);
	}
	
	private static String replaceStringBetween(String str, String open, String close, String[] replacement,
			boolean isTagIncluded) {
		if (str == null || open == null || close == null || ArrayUtil.isEmpty(replacement)) {
			return str;
		}

		StringBuilder result = new StringBuilder(str);
		String replaceStr = null;
		int maxRepLen = replacement.length - 1, lastRsLen = 0;
		int openStart = 0, openEnd = 0, closeStart = 0, closeEnd = 0, count = 0;
		while (true) {
			// ... ^open ... close ...
			openStart = result.indexOf(open, closeEnd);
			if (openStart == -1) {
				break;
			}

			// ... open^ ... close ...
			openEnd = openStart + open.length();

			// ... open ... ^close ...
			closeStart = result.indexOf(close, openEnd);
			if (closeStart == -1) {
				break;
			}

			// ... open ... close^ ...
			closeEnd = closeStart + close.length();

			lastRsLen = result.length();
			replaceStr = StringUtils.defaultString(replacement[(count > maxRepLen ? maxRepLen : count)]);

			if (isTagIncluded) {
				// ... [open ... close] ...
				result.replace(openStart, closeEnd, replaceStr);

			} else {
				// ... open[ ... ]close ...
				result.replace(openEnd, closeStart, replaceStr);
			}

			closeEnd += result.length() - lastRsLen;
			count++;
		}

		return result.toString();
	}

	public static String arrayAsString(long[] arr, String split){
		return arrayAsString(arr, split, "", "");
	}
	
	public static String arrayAsString(long[] arr, String split, String left, String right){
		StringBuffer buf = new StringBuffer();
		for (int i = 0; i < arr.length; i++) {
			if(buf.length()<=0){
				buf.append(left).append(arr[i]).append(right);
			}else{
				buf.append(split).append(left).append(arr[i]).append(right);
			}
		}
		return buf.toString();
	}
	
	public static String arrayAsString(Object[] arr, String split){
		return arrayAsString(arr, split, "", "");
	}
	
	public static String arrayAsString(Object[] arr, String split, String left, String right){
		StringBuffer buf = new StringBuffer();
		for (int i = 0; i < arr.length; i++) {
			if(isBlank(arr[i].toString())){
				continue;
			}
			if(buf.length()<=0){
				buf.append(left).append(arr[i].toString()).append(right);
			}else{
				buf.append(split).append(left).append(arr[i].toString()).append(right);
			}
		}
		return buf.toString();
	}
}